#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <algorithm>

#define FOR(i,a,n) for(int i=a,_n(n);i<_n;++i)
#define REP(i,n) FOR(i,0,n)

const int INF = 1<<29;

using namespace std;
class DLX
{
    vector<int> cnt;
    struct Node
    {
        int ri,ci;
        Node *left,*right,*up,*down;
        void Set(Node* l,Node* r,Node* u,Node* d,int a,int b)
        {
            ri = a, ci = b, left = l, right = r, up = u, down = d;
        }
    };
    vector<Node> node,row,col;
    Node head;
    int np,limit,nextLimit;
public:
    DLX(int n,int m)
    {
        node.reserve(n*m);
        row.resize(n);
        col.resize(m);
        cnt.resize(m,0);
        head.Set(&head,&head,&head,&head,n,m);
        REP(i,n)
        {
            row[i].Set(&row[i],&row[i],&head,head.down,i,m);
            row[i].down->up = &row[i];
            head.down = &row[i];
        }
        REP(i,m)
        {
            col[i].Set(&head,head.right,&col[i],&col[i],n,i);
            col[i].right->left = &col[i];
            head.right = &col[i];
        }
        np = 0;
    }
    void SetBit(int r,int c)
    {
        cnt[c]++;
        Node* nt = &node[np++];
        nt->Set(&row[r],row[r].right,&col[c],col[c].down,r,c);
        nt->right->left = nt;
        row[r].right = nt;
        nt->down->up = nt;
        col[c].down = nt;
    }
    void RemoveRow(int r)
    {
        for(Node* nc=row[r].right; nc!=&row[r]; nc=nc->right)
        {
            --cnt[nc->ci];
            nc->up->down = nc->down;
            nc->down->up = nc->up;
        }
    }
    void RemoveCol(int c)
    {
        col[c].left->right = col[c].right;
        col[c].right->left = col[c].left;
        for(Node* nr=col[c].down; nr!=&col[c]; nr=nr->down)
        {
            nr->left->right = nr->right;
            nr->right->left = nr->left;
        }
    }
    void ResumeRow(int r)
    {
        for(Node* nc=row[r].left; nc!=&row[r]; nc=nc->left)
        {
            ++cnt[nc->ci];
            nc->up->down = nc;
            nc->down->up = nc;
        }
    }
    void ResumeCol(int c)
    {
        for(Node* nr=col[c].down; nr!=&col[c]; nr=nr->down)
        {
            nr->left->right = nr;
            nr->right->left = nr;
        }
        col[c].left->right = &col[c];
        col[c].right->left = &col[c];
    }
    int H()
    {
        int ret = 0;
        for(Node* nt=head.right;nt!=&head;nt=nt->right)
        {
            ret++;
        }
        return ret>3?ret/4:1;
    }
    bool Dfs(int k)
    {
        if(head.right==&head) return true;
        int dist = k+H();
        if(dist>limit)
        {
            nextLimit = min(nextLimit,dist);
            return false;
        }
        int tc = head.right->ci;
        for(Node* nr=col[tc].down; nr!=&col[tc]; nr=nr->down)
		{
            RemoveRow(nr->ri);
            for(Node* nc=row[nr->ri].right;nc!=&row[nr->ri]; nc=nc->right)
                RemoveCol(nc->ci);
            if(Dfs(k+1)) return true;
            for(Node* nc=row[nr->ri].left;nc!=&row[nr->ri]; nc=nc->left)
                ResumeCol(nc->ci);
            ResumeRow(nr->ri);
        }
        return false;
    }
    int GetAns()
    {
        nextLimit = 1;
        while(true)
        {
            limit = nextLimit;
            nextLimit = INF;
            if(Dfs(0)) return row.size()-limit;
        }
    }
};
int main()
{
    int n,m;
    while(cin>>n>>m)
    {
        DLX dlx(n*m,n*m);
        REP(i,n) REP(j,m)
        {
            int id = i*m+j;
            dlx.SetBit(id,id);
            if(j!=m-1) dlx.SetBit(id,id+1);
            if(j!=0) dlx.SetBit(id,id-1);
            if(i!=n-1) dlx.SetBit(id,id+m);
            if(i!=0) dlx.SetBit(id,id-m);
        }
        cout<<dlx.GetAns()<<endl;
    }
    return 0;
}
